<script setup lang="ts">
import { ref, computed } from 'vue'

interface KellySimulationParams {
  initialCapital: number
  initialPositionRatio: number
  coinFlipTimes: number
  threadCount: number
  winProb: number
  winOdds: number
}



// 参数
const initialCapital = ref(100)
const initialPositionRatio = ref(0.02) // 初始头寸比例，默认2%
const coinFlipTimes = ref(1000)
const threadCount = ref(100)
const winProb = ref(0.35)
const winOdds = ref(3)

const running = ref(false)
const result = ref('')

function simulateKelly(params: KellySimulationParams) {
  const finalFunds: number[] = []
  let busts = 0

  for (let t = 0; t < params.threadCount; t++) {
    let capital = params.initialCapital
    let bust = false

    for (let i = 0; i < params.coinFlipTimes; i++) {
      if (capital <= 0) {
        bust = true
        capital = 0
        break
      }

      // 使用固定的初始头寸比例
      const bet = capital * params.initialPositionRatio
      const actualBet = Math.min(bet, capital)
      const win = Math.random() < params.winProb

      if (win) {
        capital += actualBet * (params.winOdds - 1)
      } else {
        capital -= actualBet
      }
    }

    if (bust) busts++
    finalFunds.push(capital)
  }

  return { finalFunds, busts }
}



// 计算凯利公式最优持仓比例
const optimalKellyRatio = computed(() => {
  const p = winProb.value // 胜率
  const b = winOdds.value - 1 // 净赔率（去掉本金）
  const q = 1 - p // 败率

  // 凯利公式: f* = (bp - q) / b = p - q/b
  const kellyRatio = (b * p - q) / b

  // 确保比例在合理范围内
  return Math.max(0, Math.min(1, kellyRatio))
})

// 计算期望值
const expectedValue = computed(() => {
  const p = winProb.value
  const b = winOdds.value - 1
  const q = 1 - p
  return p * b - q
})

// 判断游戏是否有正期望
const isPositiveExpectation = computed(() => expectedValue.value > 0)

async function runKellySimulation() {
  running.value = true
  result.value = ''

  await new Promise((resolve) => setTimeout(resolve, 10)) // 让UI先刷新

  const params: KellySimulationParams = {
    initialCapital: initialCapital.value,
    initialPositionRatio: initialPositionRatio.value,
    coinFlipTimes: coinFlipTimes.value,
    threadCount: threadCount.value,
    winProb: winProb.value,
    winOdds: winOdds.value,
  }

  const simulation = simulateKelly(params)

  // 统计结果
  const avg = (arr: number[]) => arr.reduce((a, b) => a + b, 0) / arr.length
  const min = (arr: number[]) => Math.min(...arr)
  const max = (arr: number[]) => Math.max(...arr)

  // 计算平均回报率
  const avgFinalFunds = avg(simulation.finalFunds)
  const avgReturnRate = ((avgFinalFunds - params.initialCapital) / params.initialCapital) * 100

  // 生成建议文本
  const generateSuggestion = () => {
    if (!isPositiveExpectation.value) {
      return '⚠️ 警告：当前参数下游戏期望值为负，建议不要参与此游戏！'
    }

    const optimal = optimalKellyRatio.value
    const current = params.initialPositionRatio
    const ratio = current / optimal

    if (ratio < 0.5) {
      return `💡 建议：您的持仓比例过于保守，可以适当增加到 ${(optimal * 100).toFixed(2)}% 以获得更好的长期收益。`
    } else if (ratio > 1.5) {
      return `⚠️ 警告：您的持仓比例过于激进，建议降低到 ${(optimal * 100).toFixed(2)}% 以降低破产风险。`
    } else if (ratio > 1.2) {
      return `⚡ 提醒：您的持仓比例略高于最优值，建议调整到 ${(optimal * 100).toFixed(2)}%。`
    } else {
      return `✅ 很好：您的持仓比例接近最优值，继续保持！`
    }
  }

  result.value =
    `胜率设置：${(params.winProb * 100).toFixed(1)}%\n` +
    `胜利赔率：${params.winOdds}倍\n` +
    `初始资金：${params.initialCapital}\n` +
    `初始头寸比例：${(params.initialPositionRatio * 100).toFixed(2)}%\n\n` +
    `📊 凯利公式分析：\n` +
    `期望值：${expectedValue.value.toFixed(4)} ${isPositiveExpectation.value ? '(正期望)' : '(负期望)'}\n` +
    `最优持仓比例：${(optimalKellyRatio.value * 100).toFixed(2)}%\n` +
    `当前/最优比例：${((params.initialPositionRatio / optimalKellyRatio.value) * 100).toFixed(1)}%\n\n` +
    `${generateSuggestion()}\n\n` +
    `🎲 模拟结果：\n` +
    `平均最终资金: ${avgFinalFunds.toFixed(2)}\n` +
    `平均回报率: ${avgReturnRate >= 0 ? '+' : ''}${avgReturnRate.toFixed(2)}%\n` +
    `最高最终资金: ${max(simulation.finalFunds).toFixed(2)}\n` +
    `最低最终资金: ${min(simulation.finalFunds).toFixed(2)}\n` +
    `爆仓次数: ${simulation.busts} 次，爆仓率: ${((simulation.busts / params.threadCount) * 100).toFixed(2)}%`

  running.value = false
}

// 生成胜率与保本赔率对照表数据
const winRateOddsTable = computed(() => {
  const tableData = []
  
  // 生成99个数据点，每行显示两组
  for (let i = 1; i <= 99; i += 2) {
    const winRate1 = i / 100
    const winRate2 = (i + 1) / 100
    
    // 计算保本所需的赔率：1/胜率
    const breakEvenOdds1 = 1 / winRate1
    const breakEvenOdds2 = winRate2 <= 0.99 ? 1 / winRate2 : null
    
    const row = {
      winRate1: i,
      breakEvenOdds1: breakEvenOdds1.toFixed(2),
      winRate2: breakEvenOdds2 ? i + 1 : null,
      breakEvenOdds2: breakEvenOdds2 ? breakEvenOdds2.toFixed(2) : null
    }
    
    tableData.push(row)
  }
  
  return tableData
})

// 应用最优比例并自动运行模拟
async function applyOptimalAndRun() {
  // 应用最优比例，保留4位小数
  initialPositionRatio.value = Math.round(optimalKellyRatio.value * 10000) / 10000

  // 等待一个微任务，确保 Vue 更新了响应式数据
  await new Promise(resolve => setTimeout(resolve, 10))

  // 自动运行模拟
  await runKellySimulation()
}


</script>

<template>
  <section class="kelly-simulator">
    <form @submit.prevent="runKellySimulation" class="sim-form">
      <h2 class="form-title">凯利公式模拟器</h2>

      <!-- 基础参数 -->
      <div class="param-group">
        <h3 class="group-title">基础参数</h3>
        <div class="form-grid">
          <div class="form-field">
            <label>初始资金</label>
            <input type="number" v-model.number="initialCapital" min="1" step="1" />
          </div>
          <div class="form-field">
            <label>每线程游戏次数</label>
            <input type="number" v-model.number="coinFlipTimes" min="1" step="1" />
          </div>
          <div class="form-field">
            <label>线程数量</label>
            <input type="number" v-model.number="threadCount" min="1" step="1" />
          </div>
        </div>
      </div>

      <!-- 概率参数 -->
      <div class="param-group">
        <h3 class="group-title">概率参数</h3>
        <div class="form-grid">
          <div class="form-field">
            <label>胜率</label>
            <input type="number" v-model.number="winProb" min="0" max="1" step="0.01" />
          </div>
          <div class="form-field">
            <label>赔率</label>
            <input type="number" v-model.number="winOdds" min="1" step="0.01" />
          </div>
        </div>
      </div>

      <!-- 头寸参数 -->
      <div class="param-group">
        <h3 class="group-title">头寸参数</h3>
        <div class="form-grid">
          <div class="form-field">
            <label>初始头寸比例</label>
            <input
              type="number"
              v-model.number="initialPositionRatio"
              min="0.0001"
              max="1"
              step="0.0001"
              title="初始下注占总资金的比例"
            />
          </div>
        </div>
      </div>

      <!-- 凯利公式建议 -->
      <div class="param-group kelly-suggestion">
        <h3 class="group-title">凯利公式建议</h3>
        <div class="suggestion-grid">
          <div class="suggestion-item">
            <span class="suggestion-label">期望值：</span>
            <span :class="['suggestion-value', isPositiveExpectation ? 'positive' : 'negative']">
              {{ expectedValue.toFixed(4) }}
              {{ isPositiveExpectation ? '(正期望)' : '(负期望)' }}
            </span>
          </div>
          <div class="suggestion-item">
            <span class="suggestion-label">最优持仓比例：</span>
            <span class="suggestion-value optimal">
              {{ (optimalKellyRatio * 100).toFixed(2) }}%
            </span>
          </div>
          <div class="suggestion-item">
            <span class="suggestion-label">建议调整：</span>
            <button
              type="button"
              @click="applyOptimalAndRun"
              class="apply-optimal-btn"
              :disabled="!isPositiveExpectation"
            >
              应用最优比例
            </button>
          </div>
        </div>
      </div>

      <div class="submit-section">
        <button type="submit" :disabled="running" class="run-button">
          <span v-if="running" class="loading-spinner"></span>
          {{ running ? '运行中...' : '开始模拟' }}
        </button>
      </div>
    </form>

    <div v-if="result" class="result-block">
      <h2 class="result-title">模拟结果</h2>
      <div class="result-content">
        <pre class="result-text">{{ result }}</pre>
      </div>
    </div>

    <!-- 胜率与保本赔率对照表 -->
    <div class="reference-table-block">
      <h2 class="table-title">📊 胜率与保本赔率对照表</h2>
      <div class="table-content">
        <div class="table-description">
          <p><strong>说明：</strong></p>
          <ul>
            <li>保本赔率是指在该胜率下期望值为0的赔率</li>
            <li>当实际赔率高于保本赔率时，游戏具有正期望值</li>
            <li>当实际赔率低于保本赔率时，游戏具有负期望值，不建议参与</li>
          </ul>
        </div>
        <div class="odds-table-container">
          <table class="odds-table">
            <thead>
              <tr>
                <th>胜率</th>
                <th>保本赔率</th>
                <th>胜率</th>
                <th>保本赔率</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="row in winRateOddsTable" :key="row.winRate1">
                <td>{{ row.winRate1 }}%</td>
                <td>{{ row.breakEvenOdds1 }}</td>
                <td v-if="row.winRate2">{{ row.winRate2 }}%</td>
                <td v-else>-</td>
                <td v-if="row.breakEvenOdds2">{{ row.breakEvenOdds2 }}</td>
                <td v-else>-</td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </section>
</template>

<style scoped>
.kelly-simulator {
  max-width: 95%;
  margin: 0 auto;
  width: 100%;
  padding: 1rem 1rem 0 1rem;
}

.sim-form {
  background: white;
  border-radius: 12px;
  padding: 1.5rem;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
  margin-bottom: 2rem;
  width: 100%;
  box-sizing: border-box;
}

.form-title {
  font-size: 1.5rem;
  color: #2c3e50;
  margin: 0 0 1.5rem 0;
  text-align: center;
  font-weight: 600;
}

.param-group {
  margin-bottom: 2rem;
  width: 100%;
}

.group-title {
  font-size: 1.1rem;
  color: #34495e;
  margin: 0 0 1rem 0;
  font-weight: 500;
  border-bottom: 2px solid #ecf0f1;
  padding-bottom: 0.5rem;
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1.2rem;
  width: 100%;
}

.form-field {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.form-field label {
  font-size: 0.9rem;
  color: #555;
  margin-bottom: 0.3rem;
  font-weight: 500;
}

.form-field input[type='number'] {
  padding: 0.6rem;
  border: 2px solid #e0e6ed;
  border-radius: 6px;
  font-size: 1rem;
  transition: all 0.3s ease;
  background: #f8f9fa;
}

.form-field input[type='number']:focus {
  outline: none;
  border-color: #667eea;
  background: white;
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}

.kelly-suggestion {
  background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
  border: 1px solid rgba(102, 126, 234, 0.2);
  color: #2c3e50;
  border-radius: 8px;
  padding: 1rem;
  margin-bottom: 2rem;
}

.kelly-suggestion .group-title {
  color: #2c3e50;
  border-bottom-color: rgba(102, 126, 234, 0.2);
}

.suggestion-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1rem;
  margin-top: 1rem;
  width: 100%;
}

.suggestion-item {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.suggestion-label {
  font-size: 0.9rem;
  opacity: 0.8;
  font-weight: 500;
  color: #555;
}

.suggestion-value {
  font-size: 1.1rem;
  font-weight: 600;
}

.suggestion-value.positive {
  color: #27ae60;
}

.suggestion-value.negative {
  color: #e74c3c;
}

.suggestion-value.optimal {
  color: #f39c12;
  font-size: 1.2rem;
}

.apply-optimal-btn {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s ease;
  font-size: 0.9rem;
}

.apply-optimal-btn:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}

.apply-optimal-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  background: #bdc3c7;
}

.submit-section {
  text-align: center;
  margin-top: 2rem;
  padding-top: 1.5rem;
  border-top: 1px solid #ecf0f1;
}

.run-button {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  padding: 0.8rem 2.5rem;
  border-radius: 8px;
  font-size: 1.1rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;
  position: relative;
  min-width: 140px;
}

.run-button:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
}

.run-button:disabled {
  background: #bdc3c7;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.loading-spinner {
  display: inline-block;
  width: 16px;
  height: 16px;
  border: 2px solid transparent;
  border-top: 2px solid white;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-right: 0.5rem;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

.result-block {
  background: white;
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
  overflow: hidden;
}

.result-title {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  margin: 0;
  padding: 1rem 1.5rem;
  font-size: 1.3rem;
  font-weight: 600;
}

.result-content {
  padding: 1.5rem;
}

.result-text {
  background: #f8f9fa;
  padding: 1rem;
  border-radius: 8px;
  font-size: 0.9rem;
  line-height: 1.6;
  margin: 0;
  overflow-x: auto;
  white-space: pre-wrap;
  word-wrap: break-word;
}

.reference-table-block {
  background: #f9f9f9;
  border-radius: 12px;
  padding: 1.5rem;
  margin-top: 2rem;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
}

.table-title {
  font-size: 1.5rem;
  color: #2c3e50;
  margin: 0 0 1.5rem 0;
  text-align: center;
  font-weight: 600;
}

.table-content {
  width: 100%;
}

.table-description {
  font-size: 0.9rem;
  color: #555;
  margin-bottom: 1.5rem;
}

.table-description ul {
  margin: 0.5rem 0;
  padding-left: 1.5rem;
}

.table-description li {
  margin-bottom: 0.3rem;
}

.odds-table-container {
  overflow-x: auto;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.odds-table {
  width: 100%;
  border-collapse: collapse;
  background: white;
  font-size: 0.9rem;
}

.odds-table th,
.odds-table td {
  padding: 0.8rem;
  text-align: center;
  border: 1px solid #e0e6ed;
}

.odds-table th {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  font-weight: 600;
  font-size: 1rem;
}

.odds-table td {
  background: white;
  color: #555;
}

.odds-table tbody tr:nth-child(even) td {
  background: #f8f9fa;
}

.odds-table tbody tr:hover td {
  background: #e3f2fd;
  color: #2c3e50;
}

@media (max-width: 768px) {
  .kelly-simulator {
    padding: 1rem 0.5rem 0 0.5rem;
  }

  .sim-form {
    padding: 1rem;
    margin: 0 0 1rem 0;
  }

  .form-title {
    font-size: 1.3rem;
  }

  .form-grid {
    grid-template-columns: 1fr;
    gap: 0.8rem;
  }

  .param-group {
    margin-bottom: 1.5rem;
  }

  .run-button {
    width: 100%;
    padding: 1rem;
    font-size: 1rem;
  }

  .result-content {
    padding: 1rem;
  }

  .result-text {
    font-size: 0.85rem;
    padding: 0.8rem;
  }

  .suggestion-grid {
    grid-template-columns: 1fr;
    gap: 0.8rem;
  }

  .kelly-suggestion {
    padding: 0.8rem;
  }

  .reference-table-block {
    padding: 1rem;
  }

  .table-title {
    font-size: 1.2rem;
  }

  .odds-table th,
  .odds-table td {
    padding: 0.6rem;
    font-size: 0.9rem;
  }
}

@media (min-width: 769px) and (max-width: 1024px) {
  .kelly-simulator {
    max-width: 95%;
    padding: 1rem 1rem 0 1rem;
  }

  .sim-form {
    padding: 1.5rem;
  }

  .form-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1.2rem;
  }

  .suggestion-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1rem;
  }

  .reference-table-block {
    padding: 1.5rem;
  }

  .table-title {
    font-size: 1.3rem;
  }

  .odds-table th,
  .odds-table td {
    padding: 0.8rem;
    font-size: 1rem;
  }
}

@media (min-width: 1025px) {
  .kelly-simulator {
    max-width: 90%;
    padding: 1rem 2rem 0 2rem;
  }

  .sim-form {
    padding: 2rem;
  }

  .form-grid {
    grid-template-columns: repeat(4, 1fr);
    gap: 1.5rem;
  }

  .suggestion-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 1.5rem;
  }

  .result-content {
    padding: 2rem;
  }

  .result-text {
    font-size: 1rem;
    padding: 1.5rem;
  }

  .reference-table-block {
    padding: 2rem;
  }

  .table-title {
    font-size: 1.5rem;
  }

  .odds-table th,
  .odds-table td {
    padding: 1rem;
    font-size: 1.1rem;
  }
}
</style>
